/*
 * Copyright 2007 Jaisy Sprl.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package be.jaisy.ui.gwt.widget.client.viewer.table;

import be.jaisy.ui.gwt.widget.client.viewer.ContentViewer;
import be.jaisy.ui.gwt.widget.client.viewer.table.internal.FilterTableViewer;
import be.jaisy.ui.gwt.widget.client.viewer.table.internal.ITableCellProvider;
import be.jaisy.ui.gwt.widget.client.viewer.table.internal.SortableHeaderTableViewer;

import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Widget;

/**
 * A table content viewer is a model-based adapter on a Composite which accesses its
 * model by means of a content provider and a table label provider.
 * 
 * This viewer has been inspired by the org.eclipse.jface project.
 * 
 * This viewer manage internaly the column sorting and row filtering.
 * 
 * Here is a sample of code:
 * <code>
 * TableViewer table = new TableViewer();
 * table.addHeader(0, "name", true);
 * table.addHeader(1, "description", false);
 * table.addFilter(0);
 * table.setContentProvider(new IContentProvider(){
 * 		public Object[] getElements(Object inputElement){
 * 			return ((List)inputElement).toArray();
 * 		}
 * 		public void inputChanged(Viewer viewer, Object oldInput, Object newInput){
 * 			// nothing to do
 * 		}
 * 	});
 * table.setLabelProvider(new ITableLabelProvider(){
 * 	public Object[] getRow(Object element){
 * 		if (element instanceof SimpleTo){
 * 			SimpleTo simpleTo = (SimpleTo)element;					
 *				return new Object[] {simpleTo.getName(), simpleTo.getDescription()};
 *			}
 *			return null;
 *		}
 *		public String getCellStyle(Object element, int columnIndex){
 *			return null;
 *		}
 *		public String getRowStyle(Object element){
 *			if (element instanceof SimpleTo){
 *				SimpleTo simpleTo = (SimpleTo)element;	
 *				if (simpleTo.getName().indexOf("?") >= 0)
 *					return "-red";
 *			}
 *			return null;
 *		}});
 *
 *		List inputs = new ArrayList();
 *		inputs.add(new SimpleTo("test1","description1"));
 *		inputs.add(new SimpleTo("test2?","description2"));
 *
 *		table.setInput(inputs);
 * 
 *  ...
 * 
 *  public class SimpleTo{
 *  	private String name;
 *    private String description;
 *    public SimpleTo(String name, String description){
 *    	this.name = name;
 *    	this.description = description;
 *    }
 *    public String getName(){return this.name;}
 *    public String getDescription(){return this.description;}    
 *  }
 * </code>
 * 
 * @see be.jaisy.ui.gwt.widget.sample.table.client.TableViewerSample
 * 
 * @author Stany Blanvalet <stany.blanvalet@jaisy.be>
 */
public class TableViewer extends ContentViewer{

	private static final String TABLEVIEWER_STYLE = "gwt-TableViewer";
	
	protected FlexTable table;

	protected ITableCellProvider tableCellProvider;

	protected SortableHeaderTableViewer sortableHeaderTableViewer;

	protected FilterTableViewer filterTableViewer;

	public TableViewer(){
		this(TABLEVIEWER_STYLE);
	}

	public TableViewer(String style){
		super();
		table = new FlexTable();
		table.setStyleName(style);
		initWidget(table);

		this.filterTableViewer = new FilterTableViewer(this);
		this.sortableHeaderTableViewer = new SortableHeaderTableViewer(this);

		this.tableCellProvider = new ITableCellProvider(){

			public void provideData(int row, int col, Object data){
				if (data == null){
					table.setText(row, col, "");
				}
				else if (data instanceof Widget){
					table.setWidget(row, col, (Widget)data);
				}
				else if (data instanceof String){
					table.setText(row, col, (String)data);
					filterTableViewer.addSuggestion(col, (String)data);
				}
				else if (data instanceof HTML){
					table.setHTML(row, col, ((HTML)data).getHTML());
				}
				else{
					table.setText(row, col, data.toString());
				}
			}

			public void provideStyle(int row, int col, String secondaryCellStyle){
				if (secondaryCellStyle != null)
					table.getCellFormatter().addStyleName(row, col, table.getStyleName() + secondaryCellStyle);
			}

			public void provideStyle(int row, String secondaryRowStyle){
				if (secondaryRowStyle != null)
					table.getRowFormatter().addStyleName(row, table.getStyleName() + secondaryRowStyle);
			}
		};
	}

	/**
	 * Add a Header name (and sort images) to the row 0 and the specific column.
	 * 
	 * @param columnIndex, int
	 * @param name String
	 * @param sortable boolean
	 */
	public void addHeader(int columnIndex, String name, boolean sortable){
		this.sortableHeaderTableViewer.addHeader(columnIndex, name, sortable);
	}

	/**
	 * Add a Filter widget to the row 1 and the specific column.
	 * 
	 * @param columnIndex, int
	 */
	public void addFilter(int columnIndex){
		filterTableViewer.addFilter(columnIndex);
	}

	/**
	 * @see be.jaisy.ui.gwt.widget.client.viewer.Viewer#refresh()
	 */
	public void refresh(){

		table.clear();
		sortableHeaderTableViewer.provideFilters(tableCellProvider);
		filterTableViewer.provideFilters(tableCellProvider);
		internalRefresh();
	}
	
	/**
	 * @see be.jaisy.ui.gwt.widget.client.viewer.Viewer#inputChanged(java.lang.Object, java.lang.Object)
	 */
	protected void inputChanged(Object input, Object oldInput){
		if (input == oldInput)
			return;
		
		this.refresh();
	}
	
	/**
	 * @see be.jaisy.ui.gwt.widget.client.viewer.Viewer#internalRefresh()
	 */
	public void internalRefresh(){

		for (int row = getFirstDataRowIndex(); row < this.table.getRowCount();)
			this.table.removeRow(row);

		Object[] cachedElement = getElements();
		Object[] filteredElements = filterTableViewer.filter(cachedElement);
		Object[] sortedElements = sortableHeaderTableViewer.sort(filteredElements);

		for (int cpt = 0, row = getFirstDataRowIndex(); cpt < sortedElements.length; cpt++, row++){
			row++;
			Object[] rowDatas = getTableLabelProvider().getRow(sortedElements[cpt]);
			for (int col = 0; col < rowDatas.length; col++){
				this.tableCellProvider.provideData(row, col, rowDatas[col]);
				String secondaryColStyle = getTableLabelProvider().getCellStyle(sortedElements[cpt], col);
				if (secondaryColStyle != null)
					this.tableCellProvider.provideStyle(row, col, secondaryColStyle);
			}
			String secondaryRowStyle = getTableLabelProvider().getRowStyle(sortedElements[cpt]);
			if (secondaryRowStyle != null)
				this.tableCellProvider.provideStyle(row, secondaryRowStyle);
		}
	}

	private int getFirstDataRowIndex(){
		return this.filterTableViewer.hasFilters() ? 2 : 1;
	}

	private ITableLabelProvider getTableLabelProvider(){
		return (ITableLabelProvider)super.getLabelProvider();
	}

	private Object[] getElements(){
		Object[] result = getContentProvider().getElements(getInput());
		if(result==null)
			result = new Object[0];
		return result;
	}
}
